﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Consul;
using Contact.API.Data;
using Contact.API.Data.Interface;
using Contact.API.Infrastructure;
using Contact.API.Models;
using Contact.API.Services;
using DnsClient;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Resilience;

namespace Contact.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<AppSettings>(Configuration);
            services.AddOptions();
            services.Configure<ServiceDiscoveryOptions>(Configuration.GetSection("ServiceDiscovery"));
            services.AddSingleton<IConsulClient>(p => new ConsulClient(cfg =>
            {
                var serviceConfiguration = p.GetRequiredService<IOptions<ServiceDiscoveryOptions>>().Value;

                if (!string.IsNullOrEmpty(serviceConfiguration.Consul.HttpEndpoint))
                {
                    // if not configured, the client will use the default value "127.0.0.1:8500"
                    cfg.Address = new Uri(serviceConfiguration.Consul.HttpEndpoint);
                }
            }));

            services.AddTransient<ContactContext>(sp=> {
                var appsettings = sp.GetRequiredService<IOptionsSnapshot<AppSettings>>();
                return new ContactContext(appsettings);
            });
            //services.Configure<ServiceDiscoveryOptions>(Configuration.GetSection("ServiceDiscovery"));
            services.AddSingleton<IDnsQuery>(p =>
            {
                return new LookupClient(IPAddress.Parse("127.0.0.1"), 8600);
            });
            services.AddSingleton<ResilienceHttpClientFactory>(sp =>
            {
                var logger = sp.GetRequiredService<ILogger<ResilienceHttpClient>>();
                var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
                return new ResilienceHttpClientFactory(logger, httpContextAccessor, 5, 5);
            });
            services.AddSingleton<IHttpClient>(sp =>
            {
                return sp.GetRequiredService<ResilienceHttpClientFactory>().GetResilienceHttpClient();
            });
            services.AddTransient<IContactRepository,MongoContactRepository>();
            services.AddTransient<IContactApplyRequestRepository, MongoContactApplyRequestRepository>();
            services.AddTransient<IUserService, UserService>();


            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(opt =>
                {
                    opt.Audience = "contact_api";
                    opt.Authority = "http://localhost:8000";
                    opt.RequireHttpsMetadata = false;
                    opt.SaveToken = true;
                });
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime, IOptions<ServiceDiscoveryOptions> serviceOptions, IConsulClient consul)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            lifetime.ApplicationStarted.Register(() =>
            {
                OnStart(app, serviceOptions, consul, lifetime);
            });
            lifetime.ApplicationStopped.Register(() =>
            {
                OnStop(app, serviceOptions, consul, lifetime);
            });
            app.UseAuthentication();
            app.UseMvc();
        }

        private void OnStop(IApplicationBuilder app, IOptions<ServiceDiscoveryOptions> serviceOptions, IConsulClient consul, IApplicationLifetime lifetime)
        {
            #region Before service discovery.
            //var client = new ConsulClient();
            //client.Agent.ServiceDeregister("user_api_127.0.0.1:8004").ConfigureAwait(false); 
            #endregion

            var features = app.Properties["server.Features"] as FeatureCollection;
            var addresses = features.Get<IServerAddressesFeature>()
                .Addresses
                .Select(p => new Uri(p));

            foreach (var address in addresses)
            {
                var serviceId = $"{serviceOptions.Value.ServiceName}_{address.Host}:{address.Port}";

                consul.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult();
            }

        }

        private void OnStart(IApplicationBuilder app, IOptions<ServiceDiscoveryOptions> serviceOptions, IConsulClient consul, IApplicationLifetime lifetime)
        {
            #region Before service discovery.
            //var client = new ConsulClient();
            //var agentServiceRegistration = new AgentServiceRegistration()
            //{
            //    ID = "user_api_127.0.0.1:8004",
            //    Address = "127.0.0.1",
            //    Name = "user_api",
            //    Port = 8004,
            //    Check = new AgentServiceCheck()
            //    {
            //        HTTP = "http://127.0.0.1:8004/api/HealthCheck/Ping",
            //        Interval = TimeSpan.FromSeconds(30),
            //        DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1)
            //    }
            //};

            //client.Agent.ServiceRegister(agentServiceRegistration).ConfigureAwait(false);
            #endregion

            var features = app.Properties["server.Features"] as FeatureCollection;
            var addresses = features.Get<IServerAddressesFeature>()
                .Addresses
                .Select(p => new Uri(p));

            foreach (var address in addresses)
            {
                var serviceId = $"{serviceOptions.Value.ServiceName}_{address.Host}:{address.Port}";

                var httpCheck = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1),
                    Interval = TimeSpan.FromSeconds(30),
                    HTTP = new Uri(address, "api/HealthCheck/Ping").OriginalString
                    //HTTP = new Uri($"{address.ToString().Replace("[::]","127.0.0.1")}api/HealthCheck/Ping").OriginalString
                };

                var registration = new AgentServiceRegistration()
                {
                    Checks = new[] { httpCheck },
                    //Address = address.Host.Equals("[::]")?"127.0.0.1": address.Host,
                    Address = address.Host,
                    ID = serviceId,
                    Name = serviceOptions.Value.ServiceName,
                    Port = address.Port
                };

                consul.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
            }

        }
    }
}
